/**
 * GameEngineLayer.m
 * 
 * 
 * Revision History: 
 *
 * Date     Author	     Version   Changes
 * ======== ============ ========= ================================================================
 * 05/08/09 PJ Cabrera   1.0       Initial version.
 * 02/17/10 Steve Hu	 1.1	   1. Commented out unrelated source codes.
 *								   2. Updated touch control area to match the background image 
 *									  design.
 *								   3. Implemented full row blocks grouping
 * 02/22/10 Robin M.     1.2	   1. Moved labels for score and level, renamed difficultyLabel
 *									  to levelNumberLabel, added "level" label above the level 
 *                                    number.
 *								   2. Implemented nextBlock feature (created initializeNextBlock 
 *                                    method, updated createNewBlock method).
 *								   3. Updated name of block1 to currentBlock for consistency.
 *								   4. Implemented current factor feature (initialized in startGame)
 *									  and created createNewFactor method for use with blocks 
 *									  dissappearing logic.
 * 02/24/10 Robin M.	 1.3	   Updated dealloc method for new labels created in v1.2.
 * 02/25/10 Robin M.	 1.4	   1. Updated nextBlock logic for new block types (negative 1-4, 
 *									  and positive 0-5).
 *								   2. Updated locations of icons/blocks for new background.
 * 02/25/10 Steve Hu	 1.5	   Updated score label spacing with brown font color image.
 * 02/25/10 Robin M.	 1.6	   Updated NextBlock display to use trimmed block images.	
 * 02/27/10 Steve Hu	 1.7	   1. Fixed the improper display of block's start dropping location 
 *									  for bug 1 in Rev105.
 *								   2. Updated control touch area coordinates to fix bug3 in Rev105.
 * 03/03/10 Olive C.     1.8       1. Add-in functions and variables for sums.
 *                                 2. Change the function of score and level.
 *                                 3. Updated the function of disappearBlocks. 
 * 03/05/10 Olive C.     1.9       Updated the display of sum number.
 *          Judy Cheng
 * 03/06/10 Steve Hu	 1.10	   1. Updated sum logic to use array instead of multilple variables
 *                                 2. Updated nextblock logic to remove some redundant codes.
 *								   3. Update sum's font image and coordinate to make it 
 *                                    center-oriented regardless of size.
 * 03/10/10 Olive C.     1.11      Update sum's coordinate to make it center-oriented all the time 
 *								   (from 2 digit to 1 digit).
 * 03/12/10 Robin Mahony 1.12      1. Updated disappearBlocks method to move all blocks down (other 
 *								   than newly created block) instantly after blocks disappear. 
 *								   2. Updated level benchmarks as well to make it harder.
 * 03/13/10 Steve Hu     1.13      Removed unrelated game logic such as Flip and commented codes.
 * 03/13/10 Robin Mahony 1.14      Made some changes to comments, removed unnecessary if statement
 * 03/15/10 Steve Hu	 1.F       ************** End of the Version 1 Implementation *************
 *
 * 03/16/10 Robin Mahony 2.1       Added pause feature, can either resume or quit from pause menu
 * 03/18/10 Robin Mahony 2.2       1. Added garbage can feature (currently only get 3 uses, and
 *								 	  have no way to get more) 
 *								   2. Now send current difficulty to block class through newBlock 
 *									  method
 *								   3. Added lines that give the ability to change a block 
 *									  image/value after it is on the field (note these lines are 
 *									 below, currently commented out change a block to a 0 block)
 * 03/19/10 Steve Hu	 2.3       1. Fixed major memory leak defect, caused by multiple redundant
 *                                    grouping initializations.
 *								   2. Removed unused BlockType variable to save memory.
 *								   3. Fixed minor memory leak defect, caused by unrelease 
 *									  currentBlock.
 * 03/20/10 Robin Mahony 2.4       1. Updated determineDifficultyIncrease to go to higher levels, and
 *								      changed level benchmarks to be double the previous level
 *								   2. Found new bug where if columns contain negative numbers, once
 *								      the columns are completed and blocks disappear, column sums can
 *									  have no blocks in them, but still have a sum > 0
 * 03/20/10 Olive C.     2.5       1. Fix the sum errors after include negative value
 *								   2. Make all blocks in the column to be 0 blocks when sum is less 
 *                                    than 0.
 * 03/21/10 Steve Hu     2.6       Implemented uploadScore function to post score data into
 *								   score MySQL database.
 * 03/21/10 Robin Mahony 2.7       1. Added check for internet connectivity that is now used in uploadScore function
 *								   2. Updated alertView method to differentiate between the pause alert, and the 
 *								      uploadScore alert to fix bug where if game is quit from pause menu, it resumes
 *									  after giving Upload alert
 * 03/23/10 Olive C.     2.8       1. Add save high score locally if enter top 5 high score list
 *								   2. Display high score list with alert with only score (need to be improved)
 * 03/25/10 Robin Mahony 2.9	   Fixed game restarting and scene switching bugs generated by 
 *						           incorrect objects release in dealloc method.
 * 03/25/10 Steve Hu	 2.10	   Dicoupled UploadScore and connectedtoNetwork into its own class.
 * 03/25/10 Judy Cheng   2.11      Updated the xblock function in computesum and computescore.
 * 03/25/10 Robin Mahony 2.12	   1. Deleted displayScore, moved saveScore to its own class.
 *								   2. Load lowest saved score, and save temp score for use with SaveScoreScene
 * 03/26/10 Olive C.     2.13      Fixed the sum add the nextblock after the xblock disappear.
 * 03/27/10 Robin Mahony 2.14	   1. Modified audio player to be in sync with the main menu's Music On/Off choice
 *								   2. Modified difficulty so that the level speeds increase less each level
 * 04/08/10 Robin Mahony 2.15      Fixed the gameover part.
 * 04/11/10 Olive C.     2.16      Fixed the bug which exist in computesums.
 * ======== ============ ========= ================================================================
 *
 */

/*Bug List:
 1. When columns containing negative numbers have blocks disappear, the column sums
 do not update correctly, causing sums > 0 even though there are no blocks in the column.
 */

#import "GameEngineLayer.h"
#import "GameScene.h"
#import "cocos2d.h"
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>

@interface GameEngineLayer (private)

- (void) startGame;
- (void) clearBoard;
- (void) initializeNextBlock;
- (void) createNewFactor;
- (void) tryToCreateNewBlocks;
- (void) createNewBlocks;
- (void) gameOver;

- (void) processTaps;
- (void) disappearBlocks;
- (void) moveBlocksDown;
- (void) findBlockGroupings;
- (void) computeScore;
- (void) determineDifficultyIncrease;
- (void) updateInfoDisplays;

- (void) computeSums;
- (void) checkMultiple;

- (void) detectStrayGrouping;
- (void) checkForBlockGroupings:(Block *)p1;

- (void) moveBlockLeft:(Block *)Block;
- (void) moveBlockRight:(Block *)Block;
- (void) moveBlockDown:(Block *)Block;
- (void) moveBlockUp:(Block *)Block;


- (void) displayHighScore;
- (void) promptToSave;


@end

@implementation GameEngineLayer

@synthesize player;
@synthesize path;


- (id) init 
{
    self = [super init];
    if (self != nil) 
	{
		/* This tells Cocos2d to call our touch event handlers.  */
		isTouchEnabled = YES;
		
		[self startGame];		
    }
    return self;
}


- (void) dealloc 
{
	NSLog(@"gameenginelayer dealloc start");
	[self removeAllChildrenWithCleanup:YES];
	[CurrentBlock release];
	[NextBlock release];
	[player stop];
	[self.player release];
	NSLog(@"gameenginelayer dealloc end");
	[super dealloc];
	
}

- (void) startGame 
{
	/* For use with checking to see if user has achieved high score.  */
	lowestScore = [[NSUserDefaults standardUserDefaults]integerForKey:@"score5Key"];
	
	/* This line retrieves the status that determines whether music was left on or off at the start menu.  */
	soundStatus = [[NSUserDefaults standardUserDefaults]boolForKey:@"musicStatus"];
	
	/* Intializes the level label, level number label, and score label.  */
	levelLabel = [Label labelWithString:@"Level" fontName:@"Marker Felt" fontSize:14];
	[levelLabel setRGB:0 :0 :0];
	levelLabel.position = ccp (25, 365); 
	[self addChild: levelLabel];
	
	levelNumberLabel = [LabelAtlas labelAtlasWithString:@"1" 
											charMapFile:@"font_white.png" itemWidth:16 itemHeight:24 
										   startCharMap:'.'];
	levelNumberLabel.position = ccp (24, 328);
	[self addChild:levelNumberLabel z:0];
	
	scoreLabel = [LabelAtlas labelAtlasWithString:@"00000" 
									  charMapFile:@"font_brown.png" itemWidth:23 itemHeight:24 
									 startCharMap:'.'];
	scoreLabel.position = ccp(204, 452);
	[self addChild:scoreLabel z:0];
	
	garbageNumLabel = [LabelAtlas labelAtlasWithString:@"3" 
										   charMapFile:@"font_white.png" itemWidth:16 itemHeight:24 
										  startCharMap:'.'];
	garbageNumLabel.position = ccp(29, 165 );
	[self addChild:garbageNumLabel z:0];
	
	/* Initializes labels for the sum of each column.  */
	for (int i = 0; i < numOfSum; i++)
	{
		sumNumberLabel[i] = [LabelAtlas labelAtlasWithString:@"0" 
												 charMapFile:@"sums_font.png" itemWidth:14 itemHeight:24 
												startCharMap:'.'];
		sumNumberLabel[i].position = ccp (72 + 32 * i, 75);
		[self addChild:sumNumberLabel[i] z:0];
	}
	
	/* Clear the board  */
	memset(board, 0, sizeof (board));
	
	[self createNewFactor];
	[self initializeNextBlock];
	[self createNewBlocks];
	
	/* Define the initial game setting variables.  */
	difficulty = 1;
	score = 0;
	frameCount = 0;
	moveCycleRatio = 45; /* Drop Blocks every 3/4 second.  */
	numGarbages = 3; /* Player starts with 3 garbage uses.  */
	oldNumGarbages = numGarbages;/* for use with updating the garbage label.  */
	baseScore = 200; /* This is used for determining the score benchmarks for each level.  */
	
	/* Define the initial game logic variables.  */
	for(int i = 0; i < numOfSum; i++)
	{
		sum[i] = 0;
		oldSum[i] = 0;
		checkX[i] = -1;
	}
	isMultiple = NO;
	countgrouping = 0;
	lastRow = NO;
	
	/* Execute updateBoard 60 times per second.  */
	[self schedule:@selector(updateBoard:) interval: 1.0 / 60.0];
	
	self.path = [[NSBundle mainBundle] pathForResource:@"gameMusic" ofType:@"mp3"];
	self.player = [[[AVAudioPlayer alloc] initWithContentsOfURL: [NSURL fileURLWithPath:self.path] error:&error] retain];
	if (!self.player) 
	{
		NSLog(@"Error %@", [error localizedDescription]); 
		return;
	}
	[self.player prepareToPlay];
	[self.player setNumberOfLoops:999999];
	
	if(soundStatus == NO)
	{
		//Do nothing since music should be turned off
	}
	else //ensures if soundStatus = YES or uninitialized, music will play
	{
		soundStatus = YES;
		[player play];
	}
	
	
}

/* Generates and displays a new target factor for use with the
 blocks dissappearing logic.  */
- (void) createNewFactor
{
	factor = (arc4random () % 4) + 2;
	
	factorLabel = [LabelAtlas labelAtlasWithString:[NSString stringWithFormat:@"%i", factor] 
									   charMapFile:@"font_brown.png" 
										 itemWidth:23 itemHeight:24 startCharMap:'.'];
	factorLabel.position = ccp (24, 100);
	[self addChild:factorLabel z:0];
}

/* Checks for Game Over condition, and calls Game Over routine if condition is met.
 Otherwise calls methods to create new blocks and compute the sums of the columns.  */
- (void) tryToCreateNewBlocks 
{
	/* Check if the spots for new Blocks are empty.  */
	if (nil != board[3][0] || nil != board[4][0]) 
	{
		/* Game over if not empty.  */
		[self promptToSave];
	} 
	else 
	{
		/* Game continue if spots are empty.  */
		[self computeSums];
		[self createNewBlocks];
	}
}

/* This method creates a block that will be used to store the
 next block (which is displayed to the user).  */
- (void) initializeNextBlock 
{
	/* Class Block is derived from Cocos2D class Sprite, which is 
	 autoreleased. We need to retain the Blocks or Cocos2D will 
	 autorelease them right from under our noses and crash the game.  */
	NSString *filename = nil, *nextType = nil;
	
	
	NextBlock = [[Block newBlock:(int) difficulty] retain];
	nextType = [NextBlock type];
	
	/* Determine filename with corresponding filetype.  */
	if (nextType) 
	{
		filename = [[NSString alloc] initWithFormat:@"t%@block.pvr", nextType];
	}
	/* Load the block image file.  */
	Sprite *bg = [Sprite spriteWithFile:filename];
	[bg setPosition:ccp(32, 405)];
	[self addChild:bg z:0];
	
	[filename release];
	[nextType release];
}

/* This method creates CurrentBlock
 and sets the default orientation.  */
- (void) createNewBlocks 
{
	/* Set the CurrentBlock to be the previous NextBlock.  */
	
	CurrentBlock = NextBlock;
	
	/* Initialize Next block.  */
	[self initializeNextBlock];
	
	/* Define the starting dropping position of the current block.  */
	board[3][0] = CurrentBlock;
	CurrentBlock.boardX = 3; CurrentBlock.boardY = 0;
	CurrentBlock.position = COMPUTE_X_Y(3,0);
	[self addChild:CurrentBlock z:2];
}

- (void) gameOver 
{
	/* Stop the game loop.  */
	[self unschedule:@selector (updateFrame:)];	
	
	UIAlertView *alert = nil;
	/* Pauses the game.  */
	[[Director sharedDirector] pause];
	alert = [[UIAlertView alloc] 
			initWithTitle:[NSString stringWithFormat:@"Game Over"]
			message:@"If you would like to play again, press Restart."
			delegate:self 
			cancelButtonTitle:@"Main Menu" 
			otherButtonTitles:@"Restart",nil];
	alert.tag = 3;
	[alert show];
	[alert release];
}

/* This method is the game logic loop. It gets called 60 times per second.  */
- (void) updateBoard:(ccTime)dt 
{
	frameCount++;
	[self processTaps];
	[self disappearBlocks];
	
	/* Execute these 60 times per second.  */
	if (frameCount % moveCycleRatio == 0) 
	{	
		[self moveBlocksDown];
		[self findBlockGroupings];
		[self computeScore];
		[self determineDifficultyIncrease];
		[self updateInfoDisplays];
	}
}

/* Define the single tap region, and listen to the events generated 
 when the user lifts their finger off the screen.  */
- (BOOL) ccTouchesEnded:(NSSet *) touches withEvent:(UIEvent *) event 
{
	
	UITouch *touch = [touches anyObject];
	CGPoint point = [touch locationInView: [touch view]];
	
	/* To compare Cocos2D Sprite positions to UITouch coordinates,
	 we need to flip UITouch y coordinate upside down for portrait mode.  */
	point.y = 480 - point.y;
	
	
    /* Define the left/right/down buttons control area to match the background image.  */
	if (((int) point.y > 2) && ((int) point.y < 66) && ((int) point.x > 128) && ((int) point.x < 192)) 
	{
		touchType = kDropBlocks;
	}
	else if (((int) point.y > 2) && ((int) point.y < 66) && ((int) point.x > 35) && ((int) point.x < 99))
	{
		touchType = kMoveLeft;
	}
	else if (((int) point.y > 2) && ((int) point.y < 66) && ((int) point.x > 218) && ((int) point.x < 282)) 
	{
		touchType = kMoveRight;
	}
	
	else if (((int) point.y > 250) && ((int) point.y < 300) && ((int) point.x > 15) && ((int) point.x < 60)) 
	{
		touchType = kPause;
	}
	
	else if (((int) point.y > 190) && ((int) point.y < 240) && ((int) point.x > 15) && ((int) point.x < 60)) 
	{
		touchType = kGarbage;
		
	}
	
	return kEventHandled;
}
/* This method examines the touchType variable that was set earlier by the 
 ccTouchesEnded:withEvent: method. The code resets the touchType variable 
 immediately so that you don't forget and are ready for the next touch event.  */
- (void) processTaps 
{	
	if (touchType == kMoveLeft) 
	{
		/* Reset touch type.  */
		touchType = kNone;
		
		Block *thisBlock;
		thisBlock = CurrentBlock;
		
		/* Move Block left if not stuck and if nothing to its left.  */
		if ((thisBlock.boardX > 0) && (!thisBlock.stuck)) 
		{
			if (nil == board[thisBlock.boardX - 1][thisBlock.boardY]) 
			{
				[self moveBlockLeft: thisBlock];
			}
		}
    } 
	else if (touchType == kMoveRight) 
	{
		/* Reset touch type.  */
		touchType = kNone;
		
		Block *thisBlock;
		thisBlock = CurrentBlock;
		
		/* Move Block right if not stuck and if nothing to its right.  */
		if ((thisBlock.boardX < kLastColumn) && (!thisBlock.stuck))
		{
			if (nil == board[thisBlock.boardX + 1][thisBlock.boardY])
			{
				[self moveBlockRight: thisBlock];
			}
		}
	} 
	else if (touchType == kDropBlocks)
	{
		/* Reset touch type.  */
		touchType = kNone;
		
		Block *thisBlock;
		thisBlock = CurrentBlock;
		
		/* If not stuck, move Block down until stuck by a block or 
		 until reaching the last row.  */
		if (!thisBlock.stuck) 
		{
			while (thisBlock.boardY != kLastRow && 
				   nil == board[thisBlock.boardX][thisBlock.boardY + 1]) 
			{
				[self moveBlockDown: thisBlock];
			}
		}
	}
	
	else if(touchType == kPause)
	{
		/* Reset touch type.  */
		touchType = kNone;
		
		/* Pauses the game.  */
		[[Director sharedDirector] pause];
		
		/* Displays a alert menu with options to Resume or Quit.  */
		UIAlertView *alert = [[UIAlertView alloc] 
							  initWithTitle:@"Pause Menu" 
							  message:@"Click OK to resume the game. Quit to end the game." 
							  delegate:self 
							  cancelButtonTitle:@"Quit" 
							  otherButtonTitles: @"Resume",@"Music On/Off",nil];
		
		alert.tag = 1; /* To differentiate this alert from the alerts that occur after uploading scores.  */
		[alert show];
		[alert release];
	}
	else if (touchType == kGarbage)
	{
		if(numGarbages > 0)
		{
			numGarbages--;
			touchType = kNone;
			/* Set current falling block to be removed, and create a new block.  */
			CurrentBlock.BlockType = 0; /* This is to ensure this blocks value will not be added to the column sum.  */
			CurrentBlock.disappearing = YES;
			x1 = CurrentBlock.boardX;
			y1 = CurrentBlock.boardY;
			/* Release the current block.  */
			[CurrentBlock release];
			CurrentBlock = nil;
			[self moveBlocksDown];
			[self tryToCreateNewBlocks];
		}
		
	}
}


- (void) moveBlockLeft:(Block *) Block 
{
	board[Block.boardX][Block.boardY] = nil;
	board[Block.boardX - 1][Block.boardY] = Block;
	Block.moveLeft;
}

- (void) moveBlockRight:(Block *) Block 
{
	board[Block.boardX][Block.boardY] = nil;
	board[Block.boardX + 1][Block.boardY] = Block;
	Block.moveRight;
}

- (void) moveBlockDown:(Block *) Block 
{
	board[Block.boardX][Block.boardY] = nil;
	board[Block.boardX][Block.boardY + 1] = Block;
	Block.moveDown;
}

- (void) moveBlockUp:(Block *) Block
{
	board[Block.boardX][Block.boardY] = nil;
	board[Block.boardX][Block.boardY - 1] = Block;
	Block.moveUp;
}


- (void) findBlockGroupings 
{
	
	
	/* This whole block looks for matching Blocks horizontally.  */
	for (int y = 0; y <= kLastRow; y++) 
	{
		
		/* Create a new current grouping set for each row.  */
		currentGrouping = [[NSMutableSet alloc] init];
		
		for (int x = 0; x <= kLastColumn; x++) 
		{
			[self checkForBlockGroupings: board[x][y]];
		}		
		
		[self detectStrayGrouping];
	}
	
	/* This whole block looks for matching Blocks vertically.  */
	for (int x = 0; x <= kLastColumn; x++) 
	{
		/* Create a new current grouping set for each column.  */
		currentGrouping = [[NSMutableSet alloc] init];
		
		for (int y = 0; y <= kLastRow; y++) 
		{
			[self checkForBlockGroupings: board[x][y]];
		} 
		
		[self detectStrayGrouping];
	}
}

/* This method adds a filled row to the grouping to be deleted upon isMultiple 
 set to YES.  */
- (void) detectStrayGrouping 
{
	/* Check if current row grouping set is full, which contains 8 blocks.  */
	if ([currentGrouping count] == (kLastColumn + 1)) 
	{
		/* Create first filled group which contain all the filled rows.  */
		if (filledGroupExist == NO)
		{
			filledGroupExist = YES;
			groupings = [[NSMutableSet alloc] init];
		}
		/* Add current row grouping set to set of all groupings.  */
		[groupings addObject:currentGrouping];
	}
	
	/* Release the current grouping set.  */
	[currentGrouping release];
	currentGrouping = nil;
}

/* Calculates the points awarded to the player when all columns have been made multiples of 
 the factor.  */
- (void) computeScore 
{
	
	/* Remove all the filled row if the isMultiple is set to YES which also means each 
	 sum is a multiple of the factor.  */ 
	if (isMultiple == YES)
	{
		/* Solve the bug that column containing X block's sum won't change until block has fallen after rows disappear.  */
		int tempFactor = factor;
		/* Regenerate a new factor.  */
		factor = (arc4random () % 4) + 2;
		/* Fix summing bug cause by negative blocks.  */
		countgrouping = groupings.count;
		int i = 0;
		for (NSSet *grouping in groupings) 
		{
			i++;
			/* Enable function of making blocks zero when sum<0 
			 if it's the last row to be deleted.  */
			if(countgrouping==i)
			{
				lastRow = YES;
			}
			for (Block *Block in grouping) 
			{
				/* X block found.  */
				if(Block.BlockType==10)
				{
					checkX[Block.boardX]--;
					Block.BlockType=0;
				}
				score += abs(Block.BlockType) * tempFactor * difficulty;
				/* Blocks in a column containing X blocks won't affect the sum when disappearing.  */
				if(checkX[Block.boardX]>0){
					Block.BlockType=0;
				}
				Block.disappearing = YES;
				/* Causes this blocks sum to be subtracted from the column sum.  */
				Block.BlockType=-(Block.BlockType);
				x1=Block.boardX;
				y1=Block.boardY;
				/* Recompute sum after each filled row is removed.  */
				[self computeSums];
			}
		}
		
		lastRow = NO;
		
		/* Release grouping sets and their contents.  */
		[groupings removeAllObjects];
		[groupings release];
		groupings = nil;
		filledGroupExist = NO;
		[currentGrouping removeAllObjects];
		[currentGrouping release];
		currentGrouping = nil;
		
		
		/* Update factor image.  */
		NSString *tempStr = [[NSString alloc] initWithFormat:@"%d", factor];
		[factorLabel setString:tempStr];
		[factorLabel draw];
		[tempStr release];
		tempStr = nil;
		
	}
	
}

/* This method is use to compute the cumulative sum for the blocks in each column.  */
- (void) computeSums
{
	Block *Block = nil;
	Block = board[x1][y1];
	/* Xblock found.  */
	if (Block.BlockType==10)
	{
		if(checkX[x1]==-1)
		{
			checkX[x1]=1;
		}
		else
		{
			checkX[x1]++;
		}
	}
	
	sum[x1] += Block.BlockType;
	
	for(int i = 0; i<8 ; i++)
	{
		/* Means this column contains Xblock(s).  */
		if (checkX[i]>0)
		{
			sum[i] = factor;
		}
		else if(checkX[i]==0)
		{
			sum[i]=0;
			/* Recalculate sum for that column  */
			for(int j=0; j<10; j++)
			{
				Block=board[i][j];
				if((Block!=nil)&&(Block.stuck==YES))
				{
					sum[i]+=Block.BlockType;
				}
			}
			checkX[i]=-1;
		}
	}	
	
	/* This method is used to ensure there is no negative sum if negative numbers are within
	 a column.  */
	if ((sum[x1] < 0 && Block.disappearing!=YES)||(sum[x1] < 0 && lastRow==YES))
	{
		sum[x1] = 0;
		for (int y = kLastRow; y >= 0; y--)
		{
			Block = board[x1][y];
			if ((Block != nil) && (Block != CurrentBlock))
			{
				Block.BlockType = 0;
				Block.type = @"0";
				Block.texture=[[TextureMgr sharedTextureMgr] addImage: @"0block.pvr"];
			}
		}
	}
	
	/* Update sum for each column.  */
	for (int i = 0; i < numOfSum; i++)
	{
		if (oldSum[i] != sum[i])
		{
			oldSum[i] = sum[i];
			NSString *tempStr = [[NSString alloc] initWithFormat:@"%01d",sum[i]];
			[sumNumberLabel[i] setString:tempStr];
			
			/* Make sure the display position of sum is always center-oriented.  */
			if (sum[i] > 9)
			{
				[sumNumberLabel[i] setPosition: ccp (64 + 32 * i, 75)];
			}
			else
			{
				[sumNumberLabel[i] setPosition: ccp (72 + 32 * i, 75)];
			}
			
			[sumNumberLabel[i] draw];
			[tempStr release];
			tempStr = nil;
		}
	}
	/* Once sum is calculated, check if sum for each rows are multiple of factor.  */
	[self checkMultiple];
}

/* This method checks each column sum to see if it is multiple of the given factor.  */
- (void) checkMultiple
{
	int checkCount = 0;
	
	for (int i = 0; i < numOfSum; i++)
	{
		if((oldSum[i] % factor == 0) && (oldSum[i] != 0))
		{	
			checkCount++;
		}
	}
	
	/* All column sum are multiple of factor, enable the isMultiple flag.  */
	if (checkCount == 8)
	{
		isMultiple = YES;
	}
	
}

/* This method sets the move cycle ratio and difficulty level according to score.  
 Once the moveCycleRatio == 1, the level will no longer change.  */
- (void) determineDifficultyIncrease 
{
	/* Sets the benchmark for each level to be double the previous benchmark.  */
	if (score >= (baseScore * pow(1.7,difficulty - 1 ))) 
	{
		/* Decreases the moveCycleRatio by five on each difficulty increase 
		 until moveCycleRatio == 20.  */
		if(moveCycleRatio > (45 - 5*difficulty) && (moveCycleRatio > 20))
		{
			moveCycleRatio -= 5;
			difficulty += 1;
		}
		/* If moveCycleRatio <= 25, then it will decrease by 3 each difficulty 
		 increase until moveCycleRatio == 5.  */
		else if(moveCycleRatio > (45 - 4*difficulty) && (moveCycleRatio > 5) )
		{
			moveCycleRatio -= 4;
			difficulty += 1;
		}
		/* If moveCycleRatio <= 5, then it will decrease by 1 each difficulty 
		 increase until moveCycleRatio == 1.  */
		else if(moveCycleRatio > 1)
		{
			moveCycleRatio -= 1;
			difficulty += 1;
		}
	}
}

/* This method assigns the dropping block to a row group set.  */ 
- (void) checkForBlockGroupings:(Block *)thisBlock 
{
	/* Check if there is a stuck, not disappearing Block at this spot.  */
	if ((thisBlock != nil) && thisBlock.stuck && !thisBlock.disappearing) 
	{
		/* Check if the current grouping set is empty or not.  */
		if ([currentGrouping count] > 0) 
		{
			/* Check the new block's level and add the block  
			 to the corresponding level group.  */
			Block *anyBlock = [currentGrouping anyObject];
			
			/* Check new block's Y coordinate of the block to determine level. */
			if (anyBlock.boardY == thisBlock.boardY) 
			{
				/* Add new block to current grouping set.  */
				[currentGrouping addObject:thisBlock];
			} 
			else 
			{
				/* It's of a different level. Current grouping ends here. 
				 Check if current row is filled up with blocks.  */
				if ([currentGrouping count] == (kLastColumn + 1)) 
				{
					/* Add current grouping row to set of all groupings.  */
					[groupings addObject:currentGrouping];
					
				}
				
				/* Release the current grouping set.  */
				[currentGrouping release];
				currentGrouping = nil;
				
				/* Create a new current grouping set, and add Block.  */
				currentGrouping = [[NSMutableSet alloc] init];
				[currentGrouping addObject:thisBlock];
				
			} 
		} 
		else 
		{
			/* Current grouping set is empty. 			
			 Add the Block to the current grouping set.  */
			[currentGrouping addObject:thisBlock];
		}
	} 
	else 
	{
		/* This position on the board is empty, or Block isn't stuck or 
		 it's disappearing. Current grouping ends here.  */
		/* Check if current row is filled up with blocks.  */
		if ([currentGrouping count] == (kLastColumn + 1)) 
		{
			/* Add current grouping set to set of all groupings.  */
			[groupings addObject:currentGrouping];
		}
		
		/*  Release the current grouping set.  */
		[currentGrouping release];
		currentGrouping = nil;
		
		/* Create a new current grouping set.  */
		currentGrouping = [[NSMutableSet alloc] init];
		
	}
}

/* This method sweeps through the board and disappears Blocks that 
 have been marked as disappearing.  It also moves the blocks on top of
 these disappearing blocks down accordingly.  */
- (void) disappearBlocks 
{	
	Block *Block = nil;
	
	/* The doneMove is used to determine when all blocks have been moved down to replace spots of
	 disappearing blocks.  */
	BOOL doneMove = FALSE; 
	
	for (int x = 0; x <= kLastColumn; x++) 
	{
		for (int y = 0; y <= kLastRow; y++) 
		{
			Block = board[x][y];
			/* Check if ihis block is disappearing.  */
			if ((Block != nil) && Block.disappearing) 
			{
				[self removeChild:Block cleanup:YES];
				Block = nil;
				board[x][y] = nil;
				
			} 
		} 
	}
	
	while(!doneMove)
	{
		doneMove = TRUE;
		
		/* Moves all blocks down immediately after the disappearing blocks are gone.  */
		for (int x = kLastColumn; x >= 0; x--) 
		{
			for (int y = kLastRow; y >= 0; y--) 
			{
				Block = board[x][y];
				
				/* Is Block "solid?" i.e. not disappearing? ,have blocks just "disappeared", 
				 also, don't want this method to move the current newly created block. Only 
				 blocks that were previously sitting atop of blocks that just disappeared.  */
				if ((Block != nil) && (!Block.disappearing) && (isMultiple == YES) 
					&& (Block != CurrentBlock))
				{
					
					/* Can this Block drop down to the next cell?  */
					if ((y!= kLastRow) && (board[x][y + 1] == nil) ) 
					{
						[self moveBlockDown:Block];
						Block.stuck = NO;
						/* if done moving blocks, all will be stuck, hence this will stay true.  */
						doneMove = FALSE; 
					} 
					else 
					{
						/* This Block can't drop anymore, it's stuck.  */
						Block.stuck = YES;
					}
				} 				
			}
		} 
	}
	
	isMultiple = NO; /* Reset isMultiple.  */
}

/* This method sweeps through the board and moves Blocks if there is an
 empty spot below them.  Used for moving the falling block the user is controlling  */
- (void) moveBlocksDown 
{	
	Block *Block = nil;
	
	for (int x = kLastColumn; x >= 0; x--) 
	{
		for (int y = kLastRow; y >= 0; y--) 
		{
			Block = board[x][y];
			
			/* Is Block "solid?" i.e. not disappearing?  */
			if ((Block != nil) && !Block.disappearing)
			{
				/* Can this Block drop down to the next cell?  */
				if ((y != kLastRow) && (board[x][y + 1] == nil)) 
				{
					[self moveBlockDown:Block];
					Block.stuck = NO;
				} 
				else 
				{
					/* This Block can't drop anymore, it's stuck.  */
					Block.stuck = YES;
				}
			}
			/* used to delete block when garbage can is pressed.  */
			else if ((Block != nil) && Block.disappearing)
			{
				
				[self removeChild:Block cleanup:YES];
				Block = nil;
				board[x][y] = nil;
				
			}
			
		}
	}
	
	if (kLastRow == CurrentBlock.boardY) 
	{
		CurrentBlock.stuck = YES;
	}
	
	/* Get new Blocks when these Blocks can't drop any more.  */
	if ( CurrentBlock.stuck)
	{
		x1 = CurrentBlock.boardX;
		y1 = CurrentBlock.boardY;
		/* Release the currentBlock.  */
		[CurrentBlock release];
		CurrentBlock = nil;
		[self tryToCreateNewBlocks];
	}
	
}

/* This method updates the game information displays such as difficulty and score.  */
- (void) updateInfoDisplays 
{
	static int oldDifficulty = 1;
	static int oldScore = 0;
	
	
	if (oldDifficulty != difficulty)
	{
		/* Give user another use of the garbage button if they have reached these certain levels. */
		if((difficulty == 3) || (difficulty == 6) || (difficulty == 9) )
		{
			numGarbages++;
		}
		
		oldDifficulty = difficulty;
		NSString *tempStr = [[NSString alloc] initWithFormat:@"%01d",difficulty];
		[levelNumberLabel setString:tempStr];
		if (difficulty>9)
		{
			[levelNumberLabel setPosition: ccp (16, 328)];
		}
		[levelNumberLabel draw];
		[tempStr release];
		tempStr = nil;
	}
	
	if (oldScore != score) 
	{
		oldScore = score;
		NSString *tempStr = [[NSString alloc] initWithFormat:@"%05d",score];
		[scoreLabel setString:tempStr];
		[scoreLabel draw];
		[tempStr release];
		tempStr = nil;
	}
	
	if (oldNumGarbages != numGarbages) 
	{
		oldNumGarbages = numGarbages;
		NSString *tempStr = [[NSString alloc] initWithFormat:@"%01d",numGarbages];
		[garbageNumLabel setString:tempStr];
		[garbageNumLabel draw];
		[tempStr release];
		tempStr = nil;
	}
}

/* This method checks users current score, and if it is a high score then
 a prompt asking if user wishes to save their score is shown.  If the user selects
 "No", then the game goes back to the start menu.  If the user selects "Yes",
 then SaveScoreScene is called which saves the score locally to the device.  */
- (void) promptToSave
{
	UIAlertView *alert = nil;
	if(score > lowestScore)
	{
		/* Pauses the game.  */
		[[Director sharedDirector] pause];
		alert = [[UIAlertView alloc] 
				 initWithTitle:[NSString stringWithFormat:@"You have a high score!"]
				 message:@"Do you wish to save your score?"
				 delegate:self 
				 cancelButtonTitle:@"Yes" 
				 otherButtonTitles:@"No",nil];
		alert.tag = 4;
		[alert show];
		[alert release];
	}
	
	else 
	{
		NSLog(@"game over but not high score");
		[self gameOver];
	}
	
	
	
}


/* This method handles all the alerts.  Alerts include Pause, Quit/GameOver, Prompt To Save Score, etc.  */
- (void) alertView: (UIAlertView *) alertView 
clickedButtonAtIndex: (NSInteger) buttonIndex 
{
	/* Alert used to handle pause menu.  */
	if(alertView.tag == 1)
	{
		switch(buttonIndex) 
		{
			case 0:
			{
				[[Director sharedDirector] resume]; /* If user quits from pause menu.  */
				/* Synchronizes music status with main menu.  */
				NSUserDefaults *boolDefault = [NSUserDefaults standardUserDefaults];
				[boolDefault setBool:soundStatus forKey:@"musicStatus"];
				[self promptToSave];
				
			}
				break;
			case 1: 
			{
				[[Director sharedDirector] resume];
			}
				break;
			case 2:
			{
				
				if (soundStatus == YES)
				{
					soundStatus = NO;
					[self.player pause];
				}
				else 
				{
					soundStatus = YES;
					[self.player play];
				}
				[[Director sharedDirector] resume];
			}
				break;
		}
		
	}
	
	/* Alert for after the game has ended, causing the user to be re-directed back to the main menu.  */
	else if (alertView.tag == 3)
	 {
		switch(buttonIndex)
		{
	 
			case 0:
			{
				[[Director sharedDirector] resume];
				[[Director sharedDirector] replaceScene: [MenuScene node]];
			}
			break;
				
			case 1:
			{
				[[Director sharedDirector] resume];
				[[Director sharedDirector] replaceScene: [GameScene node]];
			}
				break;
	 
		}
	 }
	/* Alert that gives the user the option to save their high score (if they have one).  */
	else if (alertView.tag == 4)
	{
		switch(buttonIndex)
		{
			case 0:
			{
				userSave = YES;
				[[Director sharedDirector] resume];
				NSUserDefaults *intDefault = [NSUserDefaults standardUserDefaults];
				[intDefault setInteger:score forKey:@"tempScore"];
				NSLog(@"about to call SaveScoreScene node");
				[[Director sharedDirector] replaceScene:[SaveScoreScene node]];
				//[[Director sharedDirector] replaceScene:[UploadScoreScene node]];
			}
				break;
				
			case 1:
			{
				userSave = NO;
				[[Director sharedDirector] resume];
				/* Ensures music status is synchronized with main menu.  */
				NSUserDefaults *boolDefault = [NSUserDefaults standardUserDefaults];
				[boolDefault setBool:soundStatus forKey:@"musicStatus"];
				[self gameOver];
			}
				break;
				
		}
	}
	
}


@end
